local pb = require "pb"

Buffer = {
}

function Buffer:Init(data)
	self.pos = 0
	self.data = data
	self.limit = #data
end

function Buffer:ReadTag()
	if self.pos >= self.limit then
		return 0
	end

	local tag, new_pos = pb.read_fieldId(self.data, self.pos)
	--print("Buffer:", tag, new_pos)
	self.pos = new_pos
	return tag
end

function Buffer:ReadInt()
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	self.pos = new_pos
	--print("ReadInt", val, new_pos)
	return val
end

function Buffer:ReadString()
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	local stPos = new_pos
	local endPos = new_pos+val

	self.pos = endPos
	--print("ReadStr", stPos, endPos, val)
	return string.sub(self.data, stPos+1, endPos)
end

function Buffer:ReadMsg(msgObj)
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	local stPos = new_pos
	local endPos = new_pos+val
	self.pos = stPos
	local oldLimit = self.limit
	self.limit = endPos
	msgObj:Parse(self)
	self.limit = oldLimit
	self.pos = endPos
	return msgObj
end


WriteBuffer = {
}

function WriteBuffer:Init()
	self.fields = {}
end

function WriteBuffer:WriteInt(fieldId, val)
	local tag = fieldId * 8 + 0
	local v = pb.int_encoder(tag)
	table.insert(self.fields, v)

	local v = pb.int_encoder(val)
	table.insert(self.fields, v)	
end

function WriteBuffer:WriteString(fieldId, val)
	local tag = fieldId * 8 + 2
	local v = pb.int_encoder(tag)
	table.insert(self.fields, v)

	local len = #val
	local v = pb.int_encoder(len)
	table.insert(self.fields, v)

	table.insert(self.fields, val)
end

function WriteBuffer:WriteMsg(fieldId, val)
	local tag = fieldId * 8 + 2
	local v = pb.int_encoder(tag)
	table.insert(self.fields, v)

	local buf = val:Serialize()
	local len = buf:GetSize()
	local v = pb.int_encoder(len)
	table.insert(self.fields, v)

	for k, v in ipairs(buf.fields) do
		table.insert(self.fields, v)
	end
end

function WriteBuffer:Finish( )
	return table.concat(self.fields)
end

function WriteBuffer:GetSize()
	local l = 0
	for k, v in ipairs(self.fields) do
		l = l + #v
	end
	return l
end
	


function SetIndex(obj, cla)
	local meta = {
		__index = cla,
	}
	setmetatable(obj, meta)
end

function DumpPB(obj, key)
	print("Dump", key, obj)
	for k, v in pairs(obj) do
		if type(v) == "table" then
			DumpPB(v, k)
		else
			print(key, k, type(v), v)
		end
	end
end

function CreateBuffer(st)
	local buffer = {}
	SetIndex(buffer, Buffer)
	buffer:Init(st)
	return buffer
end


